﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Net.Sockets;
using System.Net;
using System.Threading;
namespace GongZhu
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {

        #region fields
        private string userName = null;
        private string realName = null;
        private int serverPort;
        private bool isInRoom = false;
        private  IPEndPoint ip;
        private UdpClient client;
        private Thread thread;
        private List<string> roomInfos;
        private Server server;
        private string currentRoomName = null;
        private Dictionary<string, IPEndPoint> ips;
        private User[] users = new User[4];
        private User currentUser;
        private User leftUser;
        private User rightUser;
        private User topUser;
        private Card[] cards;
        private List<Card> showCards = new List<Card>();
        private List<Card> SelectedCards = new List<Card>();
        #endregion

        #region constructor
        public MainWindow()
        {
            InitializeComponent();
            roomInfos = new List<string>();
            ips = new Dictionary<string, IPEndPoint>();
        }
        #endregion

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            string username = tb_Name.Text;
            if (userName == username || username.Trim()=="")
                return;

            if (!isInRoom)
                userName = username;
            else
            {
                userName = username;
                SendRenameMessage(username);
            }
        }

        private void SendRenameMessage(string name)
        {
            //ToDo: if user is in the room, send message to the other users
            Message msg = new Message();
            msg.MessageType = MessageType.Rename;
            msg.RealName = this.realName;
            msg.UserName = name;
            byte[] bytes = msg.ToBytes();
            currentUser.UserName = name;
            client.Send(bytes, bytes.Length, ip);
        }

        private void bt_RoomSet_Click(object sender, RoutedEventArgs e)
        {
            if (bt_RoomSet.Content.ToString() == "CloseRoom")
            {
                server.Quit();
                bt_RoomSet.Content = "Set";
                return;
            }
            string roomname = tb_RoomName.Text;
            string strPort = tb_Port.Text;

            if (roomname.Trim() == "")
            {
                MessageBox.Show("RoomName can't be empty!!");
                return;
            }

            int intPort;
            if (!int.TryParse(strPort, out intPort))
            {
                MessageBox.Show("Don't you know port must be integer!!");
                return;
            }

            server = new Server(roomname,intPort);
            bt_RoomSet.Content = "CloseRoom";
        }

        private void bt_PortSet_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                if (bt_PortSet.Content.ToString() == "Reset")
                {
                    thread.Abort();
                    thread = null;
                    client.Close();
                    client = null;
                }

                string strPoint = tb_ServerPort.Text;

                int intPort;
                if (!int.TryParse(strPoint, out intPort))
                {
                    MessageBox.Show("Don't you know port must be integer!!");
                    return;
                }

                if (userName == null)
                {
                    MessageBox.Show("Please set your username!");
                    return;
                }

                this.serverPort = intPort;
                client = new UdpClient();
                Message msg = new Message();
                msg.MessageType = MessageType.RoomInfo;
                msg.UserName = userName;
                Broadcast(msg);

                thread = new Thread(Run);
                thread.IsBackground = true;
                thread.Start();
                bt_PortSet.Content = "Reset";
            }
            catch (Exception ex)
            {
                MessageBox.Show("Please make sure 8888 port is not used.");
            }
        }

        private void RoomFull(string str)
        {
            MessageBox.Show(str);//now just do it
        }

        private void Run()
        {
            Message msg;
            byte[] bytes;
            IPEndPoint ip = new IPEndPoint(IPAddress.Any,0);
            while (true)
            {
                bytes = client.Receive(ref ip);
                msg = Message.FromBytes(bytes);
                switch (msg.MessageType)
                {
                    case MessageType.RoomFull:
                        RoomFull((string)msg.Data);
                        break;
                    case MessageType.Rename:
                        //MessageBox.Show((string)msg.Data); done
                        if (realName == msg.RealName)
                        {
                            
                        }
                        else
                        {
                            User u = FindUserByRealName(msg.RealName);
                            u.UserName = msg.UserName;
                        }
                        RefreshUserInterfaceInMainThread();
                        break;
                    case MessageType.DealCard:
                        cards = SerializeHelper.FromBytes<Card[]>((byte[])msg.Data);
                        
                        //Array.Sort<Card>(cards);
                        //string str = cards.Length.ToString();
                        //for (int i = 0; i < cards.Length; i++ )
                        //{
                        //    str += cards[i].CardType+"  "+ cards[i].CardNumberType + " |||";
                        //}
                        //MessageBox.Show(str);
                        DealCardInMainThread(cards);
                            break;

                    case MessageType.NameDuplicate:
                        MessageBox.Show((string)msg.Data);
                        break;

                    case MessageType.PlayCard:

                        break;

                    case MessageType.RoomIn:
                        //User user = User.FromBytes((byte[])msg.Data);
                        //users[user.Index] = user;
                        User[] us = SerializeHelper.FromBytes<User[]>((byte[])msg.Data);
                        users = us;
                        User user = null;

                        for (int i = 0; i < users.Length; i++)
                        {
                            if (users[i] != null && users[i].UserName == this.userName)
                                user = users[i];
                        }
                            if (this.userName == msg.UserName)
                            {//self in room
                                this.realName = msg.RealName;//real name server provide for this client
                                this.ip = ip;//room server ip
                                currentRoomName = FindRoomNameByIP(ip);//keep the roomname
                                isInRoom = true;
                                RefreshRoomInfosInMainThread();//refresh the room list

                                currentUser = user;
                                //MessageBox.Show((string)msg.Data);
                            }
                            else //add member
                            {

                            }
                        RefreshUserInterfaceInMainThread();
                        break;

                    case MessageType.RoomInfo:
                        string roomInfo = (string)msg.Data;
                        if (!roomInfos.Contains(roomInfo))
                        {
                            //MessageBox.Show(roomInfo);
                            roomInfos.Add(roomInfo);
                            ips.Add(roomInfo.Split('|')[0], ip);
                            RefreshRoomInfosInMainThread();
                        }
                        break;

                    case MessageType.RoomOut:
                        
                        break;
                }
            }
        }

        private void DealCard(object obj)
        {
            Card[] cards = obj as Card[];
            Array.Sort<Card>(cards);//
            int count = cards.Length;
            //CardsPanel
            for (int i = 0; i < count; i++)
            {
                Image img = new Image();
                img.Name = cards[i].ToString();
                img.Stretch = Stretch.Fill;
                img.Width = 80;
                img.Height = 200;
                img.Margin = new Thickness(-40, 0, 0, 0);
                if (i == 0)
                {
                    img.Margin = new Thickness(0, 0, 0, 0);
                }
                img.Source = CardGenerator.Gen(cards[i]);
                CardsPanel.Children.Add(img);
                //img.MouseLeftButtonUp += BtnLeftClick;
                img.MouseRightButtonUp += BtnRightClick;
                if (!cards[i].CanShow())
                {
                    DockPanel dp = new DockPanel();
                    dp.Background = Brushes.Black;
                    dp.Opacity = 0.7;
                    dp.Margin = new Thickness(-80,0,0,0);
                    dp.Width  = 80;
                    dp.Height = 200;
                    CardsPanel.Children.Add(dp);
                }
            }            
        }

        private void BtnRightClick(object sender, RoutedEventArgs arg)
        {
            //MessageBox.Show("right");
            Image img = sender as Image;
            if (img == null)
                return;
           
            double top = img.Margin.Top;
            double left = img.Margin.Left;
            double right = img.Margin.Right;
            double bottom = img.Margin.Bottom;
            Card card = Card.FromString(img.Name);
            if (top >= 0)
            {
                img.Margin = new Thickness(left, -img.Height / 2, right, bottom);
                if (btn_PlayCard.Content.ToString() != "Play")
                {

                    showCards.Add(card);
                }
                else
                {
                    SelectedCards.Add(card);
                  
                    foreach(var v in CardsPanel.Children)
                    {
                        Image image = v as Image;
                        if (image == null)
                            continue;

                        if (img == image)
                            continue;

                        if (image.Margin.Top < 0)
                        {
                            image.Margin = new Thickness(0,0,0,0);
                        }
                    }
                }
            }
            else
            {
                img.Margin = new Thickness(left, 0, right, bottom);
                if (btn_PlayCard.Content.ToString() != "Play")
                {

                    showCards.Remove(card);
                }
                else
                {
                    SelectedCards.Remove(card);
                }
            }
        }

        private void BtnLeftClick(object sender, RoutedEventArgs arg)
        {
            //MessageBox.Show("left");
            Image img = sender as Image;
            if (img == null)
                return;
            //MessageBox.Show("left 2");
            double top = img.Margin.Top;
            if (top >= 0)
                return;

            Card card = Card.FromString(img.Name);
            List<Card> list = new List<Card>(cards);
            list.Remove(card);
            cards = list.ToArray();

            SendPlayCardMessage(card);
        }

        private void SendPlayCardMessage(Card card)
        {
            Message msg = new Message();
            msg.MessageType = MessageType.PlayCard;
            msg.RealName = currentUser.RealName;
            msg.UserName = currentUser.UserName;
            msg.Data = card;

            byte[] bytes = msg.ToBytes();
            client.Send(bytes, bytes.Length, ip);
        }

        private void DealCardInMainThread(Card[] cards)
        {
            object obj = cards;
            Application.Current.Dispatcher.Invoke(new DDealCard(DealCard), obj);
        }

        delegate void DDealCard(object obj);

        private void RefreshUserInterfaceInMainThread()
        {
            Application.Current.Dispatcher.Invoke(RefreshUserInterface);
        }
        
        private void RefreshUserInterface()
        {
            int currentUserIndex = currentUser.Index;
            int rightUserIndex = (currentUserIndex + 1) % 4;
            int leftUserIndex = (currentUserIndex + 3) % 4;
            int topUserIndex = (currentUserIndex + 2) % 4;

            leftUser = users[leftUserIndex];
            rightUser = users[rightUserIndex];
            topUser = users[topUserIndex];

            if (leftUser != null)
            {
                //Label label = new Label();
                //label.Content = leftUser.UserName;
                //Left_CardsPanel.Children.Clear();
                //Left_CardsPanel.Children.Add(label);
                label_Left.Content = leftUser.UserName;
            }

            if (rightUser != null)
            {
                //Label label = new Label();
                //label.Content = rightUser.UserName;
                //Right_CardsPanel.Children.Clear();
                //Right_CardsPanel.Children.Add(label);
                label_Right.Content = rightUser.UserName;
            }

            if(topUser!=null)
            {
                //Label label = new Label();
                //label.Content = topUser.UserName;
                //Top_CardsPanel.Children.Clear();
                //Top_CardsPanel.Children.Add(label);
                label_Top.Content = topUser.UserName;
            }

            //Label l = new Label();
            //l.Content = currentUser.UserName;
            //CardsPanel.Children.Clear();
            //CardsPanel.Children.Add(l);            
            label_Bottom.Content = currentUser.UserName;
        }

        private string FindRoomNameByIP(IPEndPoint ipadd)
        {
            foreach (var v in ips.Keys)
            {
                if (ips[v].Equals(ipadd))
                    return v;
            }

            return null;
        }

        private User FindUserByRealName(string username)
        {
            for (int i = 0; i < users.Length; i++)
            {
                if (users[i] != null && users[i].RealName == username)
                    return users[i];
            }
            return null;
        }

        /// <summary>
        /// Broadcast to find server
        /// </summary>
        /// <param name="msg"></param>
        private void Broadcast(Message msg)
        {
            IPEndPoint broadcastIP = new IPEndPoint(IPAddress.Broadcast, serverPort);
            byte[] bytes = msg.ToBytes();
            client.Send(bytes, bytes.Length, broadcastIP);
        }

        private void RefreshRoomInfos()
        {
            sp_Rooms.Children.Clear();
            string content;
            for (int i = 0; i < roomInfos.Count; i++)
            {
                Grid grid = new Grid();
                grid.ColumnDefinitions.Add(new ColumnDefinition());
                grid.ColumnDefinitions.Add(new ColumnDefinition());
                Label label = new Label();
                label.Content = roomInfos[i];
                content = roomInfos[i];
                label.SetValue(Grid.ColumnProperty, 0);
                Button btn = new Button();
                btn.Content = "Join";
                btn.SetValue(Grid.ColumnProperty, 1);
                btn.VerticalAlignment = System.Windows.VerticalAlignment.Center;
                btn.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
                if(currentRoomName == content.Split('|')[0] || content.Split('|')[1] == "4")
                    btn.IsEnabled = false;
                btn.Click += JoinButtonClick;
                btn.Name = content.Split('|')[0];
                grid.Children.Add(label);
                grid.Children.Add(btn);
                sp_Rooms.Children.Add(grid);
            }           
        }

        /// <summary>
        /// Warp RefreshRooomINfos to invoke it in main thread
        /// </summary>
        private void RefreshRoomInfosInMainThread()
        {
            Application.Current.Dispatcher.Invoke(RefreshRoomInfos);
        }
       
        private void JoinButtonClick(object sender, RoutedEventArgs e)
        {
            Message msg = new Message();
            msg.MessageType = MessageType.RoomIn;
            msg.UserName = this.userName;
            byte[] bytes = msg.ToBytes();

            Button btn = sender as Button;
            
            string roomName = btn.Name;
            client.Send(bytes, bytes.Length, ips[roomName]);
        }

        private void btn_PlayCard_Click(object sender, RoutedEventArgs e)
        {
            Button btn = sender as Button;
            if (btn.Content.ToString() == "LiangPai")
            {
                if (showCards.Count != 0)
                {
                    SendShowCardMessage();
                    ShowCard();
                    CoveryCardInterface();
                }

                btn.Content = "Play";
            }
            else
            {
                //Play
                if (SelectedCards.Count > 0)
                {
                    SendPlayCardMessage(SelectedCards[0]);
                    List<Card> list = new List<Card>(cards);
                    list.Remove(SelectedCards[0]);
                    cards = list.ToArray();
                    CoveryCardInterface();
                    Image image = new Image();
                    image.Width = 40;
                    image.Height = 100;
                    image.Stretch = Stretch.Fill;
                    image.Source = CardGenerator.Gen(SelectedCards[0]);
                    PlayedCard.Content = image;
                    SelectedCards.Clear();
                }
            }
        }


        private void SendShowCardMessage()
        {
            Message msg = new Message();
            msg.MessageType = MessageType.ShowCard;
            msg.RealName = currentUser.RealName;
            msg.UserName = currentUser.UserName;
            msg.Data = showCards.ToArray();
            byte[] bytes = msg.ToBytes();
            client.Send(bytes, bytes.Length, ip);
        }

        private void ShowCard()
        {
            //Update card interface to display the card shown
            //MessageBox.Show("show card");
            for (int i = 0; i < showCards.Count; i++)
            {

                Image img = new Image();
                img.Source = CardGenerator.Gen(showCards[i]);
                img.Width = 40;
                img.Height = 100;
                img.Stretch = Stretch.Fill;
                img.Margin = new Thickness (-20,0,0,0);
                if (i == 0)
                {
                    img.Margin = new Thickness(0,0,0,0);
                }
                ShowCardsPanel.Children.Add(img);
            }
        }

        private void RefreshCardInterface(Card card)
        {
            CardsPanel.Children.Clear();
            if (HasSameCardType(card.CardType))
            {
                //only one
                for (int i = 0; i < cards.Length; i++)
                {
                    Image img = new Image();
                    img.Source = CardGenerator.Gen(cards[i]);
                    img.Stretch = Stretch.Fill;
                    img.Width = 80;
                    img.Height = 200;
                    img.Margin = new Thickness(-40,0,0,0);
                    img.Name = cards[i].ToString();
                    if (i == 0)
                    {
                        img.Margin = new Thickness(0, 0, 0, 0);
                    }
                    CardsPanel.Children.Add(img);

                    if (cards[i].CardType != card.CardType)
                    {
                        DockPanel dp = new DockPanel();
                        dp.Background = Brushes.Black;
                        dp.Opacity = 0.7;
                        dp.Height = 200;
                        dp.Width = 80;
                        dp.Margin = new Thickness(-80, 0, 0, 0);
                        CardsPanel.Children.Add(dp);
                    }
                    else
                    {
                        img.MouseRightButtonDown += BtnRightClick;
                        img.MouseLeave += BtnLeftClick;
                    }
                }
            }
            else
            {
                for (int i = 0; i < cards.Length; i++)
                {
                    Image img = new Image();
                    img.Source = CardGenerator.Gen(cards[i]);
                    img.Stretch = Stretch.Fill;
                    img.Width = 80;
                    img.Height = 200;
                    img.Margin = new Thickness(-40, 0, 0, 0);
                    img.Name = cards[i].ToString();
                    if (i == 0)
                    {
                        img.Margin = new Thickness(0, 0, 0, 0);
                    }
                    CardsPanel.Children.Add(img);
                }
            }
        }

        private void CoveryCardInterface()
        {
            CardsPanel.Children.Clear();
            for (int i = 0; i < cards.Length; i++)
            {
                Image img = new Image();
                img.Source = CardGenerator.Gen(cards[i]);
                img.Stretch = Stretch.Fill;
                img.Width = 80;
                img.Height = 200;
                img.Margin = new Thickness(-40, 0, 0, 0);
                img.Name = cards[i].ToString();
                if (i == 0)
                {
                    img.Margin = new Thickness(0, 0, 0, 0);
                }
                CardsPanel.Children.Add(img);

                
                DockPanel dp = new DockPanel();
                dp.Background = Brushes.Black;
                dp.Opacity = 0.7;
                dp.Height = 200;
                dp.Width = 80;
                dp.Margin = new Thickness(-80, 0, 0, 0);
                CardsPanel.Children.Add(dp);
               
            }
        }

        private bool HasSameCardType(CardType ct)
        {
            foreach (var v in cards)
            {
                if (v.CardType == ct)
                    return true; 
            }
            return false;
        }

        private void DealWithPlayCardMessage(Message msg)
        {
            User user = FindUserByRealName(msg.RealName);
            if ((user.Index + 1) % 4 == currentUser.Index)//It's my turn
            {
                RefreshCardInterface((Card)msg.Data);
            }
            else
            {

            }
        }
    }
}
